1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.time;
18  
19  import java.text.ParseException;
20  import java.text.ParsePosition;
21  import java.text.SimpleDateFormat;
22  import java.util.Calendar;
23  import java.util.Date;
24  import java.util.Iterator;
25  import java.util.Locale;
26  import java.util.NoSuchElementException;
27  import java.util.concurrent.TimeUnit;
28  
29  /**
30   * <p>A suite of utilities surrounding the use of the
31   * {@link java.util.Calendar} and {@link java.util.Date} object.</p>
32   * 
33   * <p>DateUtils contains a lot of common methods considering manipulations
34   * of Dates or Calendars. Some methods require some extra explanation.
35   * The truncate, ceiling and round methods could be considered the Math.floor(),
36   * Math.ceil() or Math.round versions for dates
37   * This way date-fields will be ignored in bottom-up order.
38   * As a complement to these methods we've introduced some fragment-methods.
39   * With these methods the Date-fields will be ignored in top-down order.
40   * Since a date without a year is not a valid date, you have to decide in what
41   * kind of date-field you want your result, for instance milliseconds or days.
42   * </p>
43   * <p>
44   * Several methods are provided for adding to {@code Date} objects, of the form 
45   * {@code addXXX(Date date, int amount)}. It is important to note these methods 
46   * use a {@code Calendar} internally (with default timezone and locale) and may
47   * be affected by changes to daylight saving time (DST).
48   * </p>
49   *
50   * @since 2.0
51   * @version $Id$
52   */
53  public class DateUtils {
54  
55      /**
56       * Number of milliseconds in a standard second.
57       * @since 2.1
58       */
59      public static final long MILLIS_PER_SECOND = 1000;
60      /**
61       * Number of milliseconds in a standard minute.
62       * @since 2.1
63       */
64      public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
65      /**
66       * Number of milliseconds in a standard hour.
67       * @since 2.1
68       */
69      public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
70      /**
71       * Number of milliseconds in a standard day.
72       * @since 2.1
73       */
74      public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
75  
76      /**
77       * This is half a month, so this represents whether a date is in the top
78       * or bottom half of the month.
79       */
80      public static final int SEMI_MONTH = 1001;
81  
82      private static final int[][] fields = {
83              {Calendar.MILLISECOND},
84              {Calendar.SECOND},
85              {Calendar.MINUTE},
86              {Calendar.HOUR_OF_DAY, Calendar.HOUR},
87              {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM 
88                  /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
89              },
90              {Calendar.MONTH, DateUtils.SEMI_MONTH},
91              {Calendar.YEAR},
92              {Calendar.ERA}};
93  
94      /**
95       * A week range, starting on Sunday.
96       */
97      public static final int RANGE_WEEK_SUNDAY = 1;
98      /**
99       * A week range, starting on Monday.
100      */
101     public static final int RANGE_WEEK_MONDAY = 2;
102     /**
103      * A week range, starting on the day focused.
104      */
105     public static final int RANGE_WEEK_RELATIVE = 3;
106     /**
107      * A week range, centered around the day focused.
108      */
109     public static final int RANGE_WEEK_CENTER = 4;
110     /**
111      * A month range, the week starting on Sunday.
112      */
113     public static final int RANGE_MONTH_SUNDAY = 5;
114     /**
115      * A month range, the week starting on Monday.
116      */
117     public static final int RANGE_MONTH_MONDAY = 6;
118 
119     /**
120      * Calendar modification types.
121      */
122     private enum ModifyType {
123         /**
124          * Truncation.
125          */
126         TRUNCATE,
127         
128         /**
129          * Rounding. 
130          */
131         ROUND,
132         
133         /**
134          * Ceiling. 
135          */
136         CEILING
137     }
138 
139     /**
140      * <p>{@code DateUtils} instances should NOT be constructed in
141      * standard programming. Instead, the static methods on the class should
142      * be used, such as {@code DateUtils.parseDate(str);}.</p>
143      *
144      * <p>This constructor is public to permit tools that require a JavaBean
145      * instance to operate.</p>
146      */
147     public DateUtils() {
148         super();
149     }
150 
151     //-----------------------------------------------------------------------
152     /**
153      * <p>Checks if two date objects are on the same day ignoring time.</p>
154      *
155      * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
156      * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
157      * </p>
158      * 
159      * @param date1  the first date, not altered, not null
160      * @param date2  the second date, not altered, not null
161      * @return true if they represent the same day
162      * @throws IllegalArgumentException if either date is <code>null</code>
163      * @since 2.1
164      */
165     public static boolean isSameDay(final Date date1, final Date date2) {
166         if (date1 == null || date2 == null) {
167             throw new IllegalArgumentException("The date must not be null");
168         }
169         final Calendar cal1 = Calendar.getInstance();
170         cal1.setTime(date1);
171         final Calendar cal2 = Calendar.getInstance();
172         cal2.setTime(date2);
173         return isSameDay(cal1, cal2);
174     }
175 
176     /**
177      * <p>Checks if two calendar objects are on the same day ignoring time.</p>
178      *
179      * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
180      * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
181      * </p>
182      * 
183      * @param cal1  the first calendar, not altered, not null
184      * @param cal2  the second calendar, not altered, not null
185      * @return true if they represent the same day
186      * @throws IllegalArgumentException if either calendar is <code>null</code>
187      * @since 2.1
188      */
189     public static boolean isSameDay(final Calendar cal1, final Calendar cal2) {
190         if (cal1 == null || cal2 == null) {
191             throw new IllegalArgumentException("The date must not be null");
192         }
193         return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
194                 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
195                 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
196     }
197 
198     //-----------------------------------------------------------------------
199     /**
200      * <p>Checks if two date objects represent the same instant in time.</p>
201      *
202      * <p>This method compares the long millisecond time of the two objects.</p>
203      * 
204      * @param date1  the first date, not altered, not null
205      * @param date2  the second date, not altered, not null
206      * @return true if they represent the same millisecond instant
207      * @throws IllegalArgumentException if either date is <code>null</code>
208      * @since 2.1
209      */
210     public static boolean isSameInstant(final Date date1, final Date date2) {
211         if (date1 == null || date2 == null) {
212             throw new IllegalArgumentException("The date must not be null");
213         }
214         return date1.getTime() == date2.getTime();
215     }
216 
217     /**
218      * <p>Checks if two calendar objects represent the same instant in time.</p>
219      *
220      * <p>This method compares the long millisecond time of the two objects.</p>
221      * 
222      * @param cal1  the first calendar, not altered, not null
223      * @param cal2  the second calendar, not altered, not null
224      * @return true if they represent the same millisecond instant
225      * @throws IllegalArgumentException if either date is <code>null</code>
226      * @since 2.1
227      */
228     public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) {
229         if (cal1 == null || cal2 == null) {
230             throw new IllegalArgumentException("The date must not be null");
231         }
232         return cal1.getTime().getTime() == cal2.getTime().getTime();
233     }
234 
235     //-----------------------------------------------------------------------
236     /**
237      * <p>Checks if two calendar objects represent the same local time.</p>
238      *
239      * <p>This method compares the values of the fields of the two objects.
240      * In addition, both calendars must be the same of the same type.</p>
241      * 
242      * @param cal1  the first calendar, not altered, not null
243      * @param cal2  the second calendar, not altered, not null
244      * @return true if they represent the same millisecond instant
245      * @throws IllegalArgumentException if either date is <code>null</code>
246      * @since 2.1
247      */
248     public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) {
249         if (cal1 == null || cal2 == null) {
250             throw new IllegalArgumentException("The date must not be null");
251         }
252         return (cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) &&
253                 cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) &&
254                 cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) &&
255                 cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) &&
256                 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
257                 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
258                 cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
259                 cal1.getClass() == cal2.getClass());
260     }
261 
262     //-----------------------------------------------------------------------
263     /**
264      * <p>Parses a string representing a date by trying a variety of different parsers.</p>
265      * 
266      * <p>The parse will try each parse pattern in turn.
267      * A parse is only deemed successful if it parses the whole of the input string.
268      * If no parse patterns match, a ParseException is thrown.</p>
269      * The parser will be lenient toward the parsed date.
270      * 
271      * @param str  the date to parse, not null
272      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
273      * @return the parsed date
274      * @throws IllegalArgumentException if the date string or pattern array is null
275      * @throws ParseException if none of the date patterns were suitable (or there were none)
276      */
277     public static Date parseDate(final String str, final String... parsePatterns) throws ParseException {
278         return parseDate(str, null, parsePatterns);
279     }
280     
281     //-----------------------------------------------------------------------
282     /**
283      * <p>Parses a string representing a date by trying a variety of different parsers,
284      * using the default date format symbols for the given locale.</p>
285      * 
286      * <p>The parse will try each parse pattern in turn.
287      * A parse is only deemed successful if it parses the whole of the input string.
288      * If no parse patterns match, a ParseException is thrown.</p>
289      * The parser will be lenient toward the parsed date.
290      * 
291      * @param str  the date to parse, not null
292      * @param locale the locale whose date format symbols should be used. If <code>null</code>,
293      * the system locale is used (as per {@link #parseDate(String, String...)}).
294      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
295      * @return the parsed date
296      * @throws IllegalArgumentException if the date string or pattern array is null
297      * @throws ParseException if none of the date patterns were suitable (or there were none)
298      * @since 3.2
299      */
300     public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException {
301         return parseDateWithLeniency(str, locale, parsePatterns, true);
302     }    
303 
304   //-----------------------------------------------------------------------
305     /**
306      * <p>Parses a string representing a date by trying a variety of different parsers.</p>
307      * 
308      * <p>The parse will try each parse pattern in turn.
309      * A parse is only deemed successful if it parses the whole of the input string.
310      * If no parse patterns match, a ParseException is thrown.</p>
311      * The parser parses strictly - it does not allow for dates such as "February 942, 1996". 
312      * 
313      * @param str  the date to parse, not null
314      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
315      * @return the parsed date
316      * @throws IllegalArgumentException if the date string or pattern array is null
317      * @throws ParseException if none of the date patterns were suitable
318      * @since 2.5
319      */
320     public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException {
321         return parseDateStrictly(str, null, parsePatterns);
322     }
323 
324     /**
325      * <p>Parses a string representing a date by trying a variety of different parsers,
326      * using the default date format symbols for the given locale..</p>
327      * 
328      * <p>The parse will try each parse pattern in turn.
329      * A parse is only deemed successful if it parses the whole of the input string.
330      * If no parse patterns match, a ParseException is thrown.</p>
331      * The parser parses strictly - it does not allow for dates such as "February 942, 1996". 
332      * 
333      * @param str  the date to parse, not null
334      * @param locale the locale whose date format symbols should be used. If <code>null</code>,
335      * the system locale is used (as per {@link #parseDateStrictly(String, String...)}).
336      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
337      * @return the parsed date
338      * @throws IllegalArgumentException if the date string or pattern array is null
339      * @throws ParseException if none of the date patterns were suitable
340      * @since 3.2
341      */
342     public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException {
343         return parseDateWithLeniency(str, null, parsePatterns, false);
344     }    
345 
346     /**
347      * <p>Parses a string representing a date by trying a variety of different parsers.</p>
348      * 
349      * <p>The parse will try each parse pattern in turn.
350      * A parse is only deemed successful if it parses the whole of the input string.
351      * If no parse patterns match, a ParseException is thrown.</p>
352      * 
353      * @param str  the date to parse, not null
354      * @param locale the locale to use when interpretting the pattern, can be null in which
355      * case the default system locale is used
356      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
357      * @param lenient Specify whether or not date/time parsing is to be lenient.
358      * @return the parsed date
359      * @throws IllegalArgumentException if the date string or pattern array is null
360      * @throws ParseException if none of the date patterns were suitable
361      * @see java.util.Calendar#isLenient()
362      */
363     private static Date parseDateWithLeniency(
364             final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException {
365         if (str == null || parsePatterns == null) {
366             throw new IllegalArgumentException("Date and Patterns must not be null");
367         }
368         
369         SimpleDateFormat parser;
370         if (locale == null) {
371             parser = new SimpleDateFormat();
372         } else {
373             parser = new SimpleDateFormat("", locale);
374         }
375         
376         parser.setLenient(lenient);
377         final ParsePosition pos = new ParsePosition(0);
378         for (final String parsePattern : parsePatterns) {
379 
380             String pattern = parsePattern;
381 
382             // LANG-530 - need to make sure 'ZZ' output doesn't get passed to SimpleDateFormat
383             if (parsePattern.endsWith("ZZ")) {
384                 pattern = pattern.substring(0, pattern.length() - 1);
385             }
386             
387             parser.applyPattern(pattern);
388             pos.setIndex(0);
389 
390             String str2 = str;
391             // LANG-530 - need to make sure 'ZZ' output doesn't hit SimpleDateFormat as it will ParseException
392             if (parsePattern.endsWith("ZZ")) {
393                 str2 = str.replaceAll("([-+][0-9][0-9]):([0-9][0-9])$", "$1$2"); 
394             }
395 
396             final Date date = parser.parse(str2, pos);
397             if (date != null && pos.getIndex() == str2.length()) {
398                 return date;
399             }
400         }
401         throw new ParseException("Unable to parse the date: " + str, -1);
402     }
403 
404     //-----------------------------------------------------------------------
405     /**
406      * Adds a number of years to a date returning a new object.
407      * The original {@code Date} is unchanged.
408      *
409      * @param date  the date, not null
410      * @param amount  the amount to add, may be negative
411      * @return the new {@code Date} with the amount added
412      * @throws IllegalArgumentException if the date is null
413      */
414     public static Date addYears(final Date date, final int amount) {
415         return add(date, Calendar.YEAR, amount);
416     }
417 
418     //-----------------------------------------------------------------------
419     /**
420      * Adds a number of months to a date returning a new object.
421      * The original {@code Date} is unchanged.
422      *
423      * @param date  the date, not null
424      * @param amount  the amount to add, may be negative
425      * @return the new {@code Date} with the amount added
426      * @throws IllegalArgumentException if the date is null
427      */
428     public static Date addMonths(final Date date, final int amount) {
429         return add(date, Calendar.MONTH, amount);
430     }
431 
432     //-----------------------------------------------------------------------
433     /**
434      * Adds a number of weeks to a date returning a new object.
435      * The original {@code Date} is unchanged.
436      *
437      * @param date  the date, not null
438      * @param amount  the amount to add, may be negative
439      * @return the new {@code Date} with the amount added
440      * @throws IllegalArgumentException if the date is null
441      */
442     public static Date addWeeks(final Date date, final int amount) {
443         return add(date, Calendar.WEEK_OF_YEAR, amount);
444     }
445 
446     //-----------------------------------------------------------------------
447     /**
448      * Adds a number of days to a date returning a new object.
449      * The original {@code Date} is unchanged.
450      *
451      * @param date  the date, not null
452      * @param amount  the amount to add, may be negative
453      * @return the new {@code Date} with the amount added
454      * @throws IllegalArgumentException if the date is null
455      */
456     public static Date addDays(final Date date, final int amount) {
457         return add(date, Calendar.DAY_OF_MONTH, amount);
458     }
459 
460     //-----------------------------------------------------------------------
461     /**
462      * Adds a number of hours to a date returning a new object.
463      * The original {@code Date} is unchanged.
464      *
465      * @param date  the date, not null
466      * @param amount  the amount to add, may be negative
467      * @return the new {@code Date} with the amount added
468      * @throws IllegalArgumentException if the date is null
469      */
470     public static Date addHours(final Date date, final int amount) {
471         return add(date, Calendar.HOUR_OF_DAY, amount);
472     }
473 
474     //-----------------------------------------------------------------------
475     /**
476      * Adds a number of minutes to a date returning a new object.
477      * The original {@code Date} is unchanged.
478      *
479      * @param date  the date, not null
480      * @param amount  the amount to add, may be negative
481      * @return the new {@code Date} with the amount added
482      * @throws IllegalArgumentException if the date is null
483      */
484     public static Date addMinutes(final Date date, final int amount) {
485         return add(date, Calendar.MINUTE, amount);
486     }
487 
488     //-----------------------------------------------------------------------
489     /**
490      * Adds a number of seconds to a date returning a new object.
491      * The original {@code Date} is unchanged.
492      *
493      * @param date  the date, not null
494      * @param amount  the amount to add, may be negative
495      * @return the new {@code Date} with the amount added
496      * @throws IllegalArgumentException if the date is null
497      */
498     public static Date addSeconds(final Date date, final int amount) {
499         return add(date, Calendar.SECOND, amount);
500     }
501 
502     //-----------------------------------------------------------------------
503     /**
504      * Adds a number of milliseconds to a date returning a new object.
505      * The original {@code Date} is unchanged.
506      *
507      * @param date  the date, not null
508      * @param amount  the amount to add, may be negative
509      * @return the new {@code Date} with the amount added
510      * @throws IllegalArgumentException if the date is null
511      */
512     public static Date addMilliseconds(final Date date, final int amount) {
513         return add(date, Calendar.MILLISECOND, amount);
514     }
515 
516     //-----------------------------------------------------------------------
517     /**
518      * Adds to a date returning a new object.
519      * The original {@code Date} is unchanged.
520      *
521      * @param date  the date, not null
522      * @param calendarField  the calendar field to add to
523      * @param amount  the amount to add, may be negative
524      * @return the new {@code Date} with the amount added
525      * @throws IllegalArgumentException if the date is null
526      */
527     private static Date add(final Date date, final int calendarField, final int amount) {
528         if (date == null) {
529             throw new IllegalArgumentException("The date must not be null");
530         }
531         final Calendar c = Calendar.getInstance();
532         c.setTime(date);
533         c.add(calendarField, amount);
534         return c.getTime();
535     }
536     
537     //-----------------------------------------------------------------------
538     /**
539      * Sets the years field to a date returning a new object.
540      * The original {@code Date} is unchanged.
541      *
542      * @param date  the date, not null
543      * @param amount the amount to set
544      * @return a new {@code Date} set with the specified value
545      * @throws IllegalArgumentException if the date is null
546      * @since 2.4
547      */
548     public static Date setYears(final Date date, final int amount) {
549         return set(date, Calendar.YEAR, amount);
550     }
551 
552     //-----------------------------------------------------------------------
553     /**
554      * Sets the months field to a date returning a new object.
555      * The original {@code Date} is unchanged.
556      *
557      * @param date  the date, not null
558      * @param amount the amount to set
559      * @return a new {@code Date} set with the specified value
560      * @throws IllegalArgumentException if the date is null
561      * @since 2.4
562      */
563     public static Date setMonths(final Date date, final int amount) {
564         return set(date, Calendar.MONTH, amount);
565     }
566 
567     //-----------------------------------------------------------------------
568     /**
569      * Sets the day of month field to a date returning a new object.
570      * The original {@code Date} is unchanged.
571      *
572      * @param date  the date, not null
573      * @param amount the amount to set
574      * @return a new {@code Date} set with the specified value
575      * @throws IllegalArgumentException if the date is null
576      * @since 2.4
577      */
578     public static Date setDays(final Date date, final int amount) {
579         return set(date, Calendar.DAY_OF_MONTH, amount);
580     }
581 
582     //-----------------------------------------------------------------------
583     /**
584      * Sets the hours field to a date returning a new object.  Hours range 
585      * from  0-23.
586      * The original {@code Date} is unchanged.
587      *
588      * @param date  the date, not null
589      * @param amount the amount to set
590      * @return a new {@code Date} set with the specified value
591      * @throws IllegalArgumentException if the date is null
592      * @since 2.4
593      */
594     public static Date setHours(final Date date, final int amount) {
595         return set(date, Calendar.HOUR_OF_DAY, amount);
596     }
597 
598     //-----------------------------------------------------------------------
599     /**
600      * Sets the minute field to a date returning a new object.
601      * The original {@code Date} is unchanged.
602      *
603      * @param date  the date, not null
604      * @param amount the amount to set
605      * @return a new {@code Date} set with the specified value
606      * @throws IllegalArgumentException if the date is null
607      * @since 2.4
608      */
609     public static Date setMinutes(final Date date, final int amount) {
610         return set(date, Calendar.MINUTE, amount);
611     }
612     
613     //-----------------------------------------------------------------------
614     /**
615      * Sets the seconds field to a date returning a new object.
616      * The original {@code Date} is unchanged.
617      *
618      * @param date  the date, not null
619      * @param amount the amount to set
620      * @return a new {@code Date} set with the specified value
621      * @throws IllegalArgumentException if the date is null
622      * @since 2.4
623      */
624     public static Date setSeconds(final Date date, final int amount) {
625         return set(date, Calendar.SECOND, amount);
626     }
627 
628     //-----------------------------------------------------------------------
629     /**
630      * Sets the miliseconds field to a date returning a new object.
631      * The original {@code Date} is unchanged.
632      *
633      * @param date  the date, not null
634      * @param amount the amount to set
635      * @return a new {@code Date} set with the specified value
636      * @throws IllegalArgumentException if the date is null
637      * @since 2.4
638      */
639     public static Date setMilliseconds(final Date date, final int amount) {
640         return set(date, Calendar.MILLISECOND, amount);
641     } 
642     
643     //-----------------------------------------------------------------------
644     /**
645      * Sets the specified field to a date returning a new object.  
646      * This does not use a lenient calendar.
647      * The original {@code Date} is unchanged.
648      *
649      * @param date  the date, not null
650      * @param calendarField  the {@code Calendar} field to set the amount to
651      * @param amount the amount to set
652      * @return a new {@code Date} set with the specified value
653      * @throws IllegalArgumentException if the date is null
654      * @since 2.4
655      */
656     private static Date set(final Date date, final int calendarField, final int amount) {
657         if (date == null) {
658             throw new IllegalArgumentException("The date must not be null");
659         }
660         // getInstance() returns a new object, so this method is thread safe.
661         final Calendar c = Calendar.getInstance();
662         c.setLenient(false);
663         c.setTime(date);
664         c.set(calendarField, amount);
665         return c.getTime();
666     }   
667 
668     //-----------------------------------------------------------------------
669     /**
670      * Converts a {@code Date} into a {@code Calendar}. 
671      * 
672      * @param date the date to convert to a Calendar
673      * @return the created Calendar
674      * @throws NullPointerException if null is passed in
675      * @since 3.0
676      */
677     public static Calendar toCalendar(final Date date) {
678         final Calendar c = Calendar.getInstance();
679         c.setTime(date);
680         return c;
681     }
682     
683     //-----------------------------------------------------------------------
684     /**
685      * <p>Rounds a date, leaving the field specified as the most
686      * significant field.</p>
687      *
688      * <p>For example, if you had the date-time of 28 Mar 2002
689      * 13:45:01.231, if this was passed with HOUR, it would return
690      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
691      * would return 1 April 2002 0:00:00.000.</p>
692      * 
693      * <p>For a date in a timezone that handles the change to daylight
694      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
695      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a 
696      * date that crosses this time would produce the following values:
697      * </p>
698      * <ul>
699      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
700      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
701      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
702      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
703      * </ul>
704      * 
705      * @param date  the date to work with, not null
706      * @param field  the field from {@code Calendar} or {@code SEMI_MONTH}
707      * @return the different rounded date, not null
708      * @throws ArithmeticException if the year is over 280 million
709      */
710     public static Date round(final Date date, final int field) {
711         if (date == null) {
712             throw new IllegalArgumentException("The date must not be null");
713         }
714         final Calendar gval = Calendar.getInstance();
715         gval.setTime(date);
716         modify(gval, field, ModifyType.ROUND);
717         return gval.getTime();
718     }
719 
720     /**
721      * <p>Rounds a date, leaving the field specified as the most
722      * significant field.</p>
723      *
724      * <p>For example, if you had the date-time of 28 Mar 2002
725      * 13:45:01.231, if this was passed with HOUR, it would return
726      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
727      * would return 1 April 2002 0:00:00.000.</p>
728      * 
729      * <p>For a date in a timezone that handles the change to daylight
730      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
731      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a 
732      * date that crosses this time would produce the following values:
733      * </p>
734      * <ul>
735      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
736      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
737      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
738      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
739      * </ul>
740      * 
741      * @param date  the date to work with, not null
742      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
743      * @return the different rounded date, not null
744      * @throws IllegalArgumentException if the date is <code>null</code>
745      * @throws ArithmeticException if the year is over 280 million
746      */
747     public static Calendar round(final Calendar date, final int field) {
748         if (date == null) {
749             throw new IllegalArgumentException("The date must not be null");
750         }
751         final Calendar rounded = (Calendar) date.clone();
752         modify(rounded, field, ModifyType.ROUND);
753         return rounded;
754     }
755 
756     /**
757      * <p>Rounds a date, leaving the field specified as the most
758      * significant field.</p>
759      *
760      * <p>For example, if you had the date-time of 28 Mar 2002
761      * 13:45:01.231, if this was passed with HOUR, it would return
762      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
763      * would return 1 April 2002 0:00:00.000.</p>
764      * 
765      * <p>For a date in a timezone that handles the change to daylight
766      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
767      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a 
768      * date that crosses this time would produce the following values:
769      * </p>
770      * <ul>
771      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
772      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
773      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
774      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
775      * </ul>
776      * 
777      * @param date  the date to work with, either {@code Date} or {@code Calendar}, not null
778      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
779      * @return the different rounded date, not null
780      * @throws IllegalArgumentException if the date is <code>null</code>
781      * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar}
782      * @throws ArithmeticException if the year is over 280 million
783      */
784     public static Date round(final Object date, final int field) {
785         if (date == null) {
786             throw new IllegalArgumentException("The date must not be null");
787         }
788         if (date instanceof Date) {
789             return round((Date) date, field);
790         } else if (date instanceof Calendar) {
791             return round((Calendar) date, field).getTime();
792         } else {
793             throw new ClassCastException("Could not round " + date);
794         }
795     }
796 
797     //-----------------------------------------------------------------------
798     /**
799      * <p>Truncates a date, leaving the field specified as the most
800      * significant field.</p>
801      *
802      * <p>For example, if you had the date-time of 28 Mar 2002
803      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
804      * 2002 13:00:00.000.  If this was passed with MONTH, it would
805      * return 1 Mar 2002 0:00:00.000.</p>
806      * 
807      * @param date  the date to work with, not null
808      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
809      * @return the different truncated date, not null
810      * @throws IllegalArgumentException if the date is <code>null</code>
811      * @throws ArithmeticException if the year is over 280 million
812      */
813     public static Date truncate(final Date date, final int field) {
814         if (date == null) {
815             throw new IllegalArgumentException("The date must not be null");
816         }
817         final Calendar gval = Calendar.getInstance();
818         gval.setTime(date);
819         modify(gval, field, ModifyType.TRUNCATE);
820         return gval.getTime();
821     }
822 
823     /**
824      * <p>Truncates a date, leaving the field specified as the most
825      * significant field.</p>
826      *
827      * <p>For example, if you had the date-time of 28 Mar 2002
828      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
829      * 2002 13:00:00.000.  If this was passed with MONTH, it would
830      * return 1 Mar 2002 0:00:00.000.</p>
831      * 
832      * @param date  the date to work with, not null
833      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
834      * @return the different truncated date, not null
835      * @throws IllegalArgumentException if the date is <code>null</code>
836      * @throws ArithmeticException if the year is over 280 million
837      */
838     public static Calendar truncate(final Calendar date, final int field) {
839         if (date == null) {
840             throw new IllegalArgumentException("The date must not be null");
841         }
842         final Calendar truncated = (Calendar) date.clone();
843         modify(truncated, field, ModifyType.TRUNCATE);
844         return truncated;
845     }
846 
847     /**
848      * <p>Truncates a date, leaving the field specified as the most
849      * significant field.</p>
850      *
851      * <p>For example, if you had the date-time of 28 Mar 2002
852      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
853      * 2002 13:00:00.000.  If this was passed with MONTH, it would
854      * return 1 Mar 2002 0:00:00.000.</p>
855      * 
856      * @param date  the date to work with, either {@code Date} or {@code Calendar}, not null
857      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
858      * @return the different truncated date, not null
859      * @throws IllegalArgumentException if the date is <code>null</code>
860      * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar}
861      * @throws ArithmeticException if the year is over 280 million
862      */
863     public static Date truncate(final Object date, final int field) {
864         if (date == null) {
865             throw new IllegalArgumentException("The date must not be null");
866         }
867         if (date instanceof Date) {
868             return truncate((Date) date, field);
869         } else if (date instanceof Calendar) {
870             return truncate((Calendar) date, field).getTime();
871         } else {
872             throw new ClassCastException("Could not truncate " + date);
873         }
874     }
875     
876   //-----------------------------------------------------------------------
877     /**
878      * <p>Gets a date ceiling, leaving the field specified as the most
879      * significant field.</p>
880      *
881      * <p>For example, if you had the date-time of 28 Mar 2002
882      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
883      * 2002 14:00:00.000.  If this was passed with MONTH, it would
884      * return 1 Apr 2002 0:00:00.000.</p>
885      * 
886      * @param date  the date to work with, not null
887      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
888      * @return the different ceil date, not null
889      * @throws IllegalArgumentException if the date is <code>null</code>
890      * @throws ArithmeticException if the year is over 280 million
891      * @since 2.5
892      */
893     public static Date ceiling(final Date date, final int field) {
894         if (date == null) {
895             throw new IllegalArgumentException("The date must not be null");
896         }
897         final Calendar gval = Calendar.getInstance();
898         gval.setTime(date);
899         modify(gval, field, ModifyType.CEILING);
900         return gval.getTime();
901     }
902 
903     /**
904      * <p>Gets a date ceiling, leaving the field specified as the most
905      * significant field.</p>
906      *
907      * <p>For example, if you had the date-time of 28 Mar 2002
908      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
909      * 2002 14:00:00.000.  If this was passed with MONTH, it would
910      * return 1 Apr 2002 0:00:00.000.</p>
911      * 
912      * @param date  the date to work with, not null
913      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
914      * @return the different ceil date, not null
915      * @throws IllegalArgumentException if the date is <code>null</code>
916      * @throws ArithmeticException if the year is over 280 million
917      * @since 2.5
918      */
919     public static Calendar ceiling(final Calendar date, final int field) {
920         if (date == null) {
921             throw new IllegalArgumentException("The date must not be null");
922         }
923         final Calendar ceiled = (Calendar) date.clone();
924         modify(ceiled, field, ModifyType.CEILING);
925         return ceiled;
926     }
927 
928     /**
929      * <p>Gets a date ceiling, leaving the field specified as the most
930      * significant field.</p>
931      *
932      * <p>For example, if you had the date-time of 28 Mar 2002
933      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
934      * 2002 14:00:00.000.  If this was passed with MONTH, it would
935      * return 1 Apr 2002 0:00:00.000.</p>
936      * 
937      * @param date  the date to work with, either {@code Date} or {@code Calendar}, not null
938      * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
939      * @return the different ceil date, not null
940      * @throws IllegalArgumentException if the date is <code>null</code>
941      * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar}
942      * @throws ArithmeticException if the year is over 280 million
943      * @since 2.5
944      */
945     public static Date ceiling(final Object date, final int field) {
946         if (date == null) {
947             throw new IllegalArgumentException("The date must not be null");
948         }
949         if (date instanceof Date) {
950             return ceiling((Date) date, field);
951         } else if (date instanceof Calendar) {
952             return ceiling((Calendar) date, field).getTime();
953         } else {
954             throw new ClassCastException("Could not find ceiling of for type: " + date.getClass());
955         }
956     }
957 
958     //-----------------------------------------------------------------------
959     /**
960      * <p>Internal calculation method.</p>
961      * 
962      * @param val  the calendar, not null
963      * @param field  the field constant
964      * @param modType  type to truncate, round or ceiling
965      * @throws ArithmeticException if the year is over 280 million
966      */
967     private static void modify(final Calendar val, final int field, final ModifyType modType) {
968         if (val.get(Calendar.YEAR) > 280000000) {
969             throw new ArithmeticException("Calendar value too large for accurate calculations");
970         }
971         
972         if (field == Calendar.MILLISECOND) {
973             return;
974         }
975 
976         // ----------------- Fix for LANG-59 ---------------------- START ---------------
977         // see http://issues.apache.org/jira/browse/LANG-59
978         //
979         // Manually truncate milliseconds, seconds and minutes, rather than using
980         // Calendar methods.
981 
982         final Date date = val.getTime();
983         long time = date.getTime();
984         boolean done = false;
985 
986         // truncate milliseconds
987         final int millisecs = val.get(Calendar.MILLISECOND);
988         if (ModifyType.TRUNCATE == modType || millisecs < 500) {
989             time = time - millisecs;
990         }
991         if (field == Calendar.SECOND) {
992             done = true;
993         }
994 
995         // truncate seconds
996         final int seconds = val.get(Calendar.SECOND);
997         if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) {
998             time = time - (seconds * 1000L);
999         }
1000         if (field == Calendar.MINUTE) {
1001             done = true;
1002         }
1003 
1004         // truncate minutes
1005         final int minutes = val.get(Calendar.MINUTE);
1006         if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) {
1007             time = time - (minutes * 60000L);
1008         }
1009 
1010         // reset time
1011         if (date.getTime() != time) {
1012             date.setTime(time);
1013             val.setTime(date);
1014         }
1015         // ----------------- Fix for LANG-59 ----------------------- END ----------------
1016 
1017         boolean roundUp = false;
1018         for (final int[] aField : fields) {
1019             for (final int element : aField) {
1020                 if (element == field) {
1021                     //This is our field... we stop looping
1022                     if (modType == ModifyType.CEILING || (modType == ModifyType.ROUND && roundUp)) {
1023                         if (field == DateUtils.SEMI_MONTH) {
1024                             //This is a special case that's hard to generalize
1025                             //If the date is 1, we round up to 16, otherwise
1026                             //  we subtract 15 days and add 1 month
1027                             if (val.get(Calendar.DATE) == 1) {
1028                                 val.add(Calendar.DATE, 15);
1029                             } else {
1030                                 val.add(Calendar.DATE, -15);
1031                                 val.add(Calendar.MONTH, 1);
1032                             }
1033 // ----------------- Fix for LANG-440 ---------------------- START ---------------
1034                         } else if (field == Calendar.AM_PM) {
1035                             // This is a special case
1036                             // If the time is 0, we round up to 12, otherwise
1037                             //  we subtract 12 hours and add 1 day
1038                             if (val.get(Calendar.HOUR_OF_DAY) == 0) {
1039                                 val.add(Calendar.HOUR_OF_DAY, 12);
1040                             } else {
1041                                 val.add(Calendar.HOUR_OF_DAY, -12);
1042                                 val.add(Calendar.DATE, 1);
1043                             }
1044 // ----------------- Fix for LANG-440 ---------------------- END ---------------
1045                         } else {
1046                             //We need at add one to this field since the
1047                             //  last number causes us to round up
1048                             val.add(aField[0], 1);
1049                         }
1050                     }
1051                     return;
1052                 }
1053             }
1054             //We have various fields that are not easy roundings
1055             int offset = 0;
1056             boolean offsetSet = false;
1057             //These are special types of fields that require different rounding rules
1058             switch (field) {
1059                 case DateUtils.SEMI_MONTH:
1060                     if (aField[0] == Calendar.DATE) {
1061                         //If we're going to drop the DATE field's value,
1062                         //  we want to do this our own way.
1063                         //We need to subtrace 1 since the date has a minimum of 1
1064                         offset = val.get(Calendar.DATE) - 1;
1065                         //If we're above 15 days adjustment, that means we're in the
1066                         //  bottom half of the month and should stay accordingly.
1067                         if (offset >= 15) {
1068                             offset -= 15;
1069                         }
1070                         //Record whether we're in the top or bottom half of that range
1071                         roundUp = offset > 7;
1072                         offsetSet = true;
1073                     }
1074                     break;
1075                 case Calendar.AM_PM:
1076                     if (aField[0] == Calendar.HOUR_OF_DAY) {
1077                         //If we're going to drop the HOUR field's value,
1078                         //  we want to do this our own way.
1079                         offset = val.get(Calendar.HOUR_OF_DAY);
1080                         if (offset >= 12) {
1081                             offset -= 12;
1082                         }
1083                         roundUp = offset >= 6;
1084                         offsetSet = true;
1085                     }
1086                     break;
1087                 default:
1088                     break;
1089             }
1090             if (!offsetSet) {
1091                 final int min = val.getActualMinimum(aField[0]);
1092                 final int max = val.getActualMaximum(aField[0]);
1093                 //Calculate the offset from the minimum allowed value
1094                 offset = val.get(aField[0]) - min;
1095                 //Set roundUp if this is more than half way between the minimum and maximum
1096                 roundUp = offset > ((max - min) / 2);
1097             }
1098             //We need to remove this field
1099             if (offset != 0) {
1100                 val.set(aField[0], val.get(aField[0]) - offset);
1101             }
1102         }
1103         throw new IllegalArgumentException("The field " + field + " is not supported");
1104 
1105     }
1106 
1107     //-----------------------------------------------------------------------
1108     /**
1109      * <p>Constructs an <code>Iterator</code> over each day in a date
1110      * range defined by a focus date and range style.</p>
1111      *
1112      * <p>For instance, passing Thursday, July 4, 2002 and a
1113      * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
1114      * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1115      * 2002, returning a Calendar instance for each intermediate day.</p>
1116      *
1117      * <p>This method provides an iterator that returns Calendar objects.
1118      * The days are progressed using {@link Calendar#add(int, int)}.</p>
1119      *
1120      * @param focus  the date to work with, not null
1121      * @param rangeStyle  the style constant to use. Must be one of
1122      * {@link DateUtils#RANGE_MONTH_SUNDAY}, 
1123      * {@link DateUtils#RANGE_MONTH_MONDAY},
1124      * {@link DateUtils#RANGE_WEEK_SUNDAY},
1125      * {@link DateUtils#RANGE_WEEK_MONDAY},
1126      * {@link DateUtils#RANGE_WEEK_RELATIVE},
1127      * {@link DateUtils#RANGE_WEEK_CENTER}
1128      * @return the date iterator, not null, not null
1129      * @throws IllegalArgumentException if the date is <code>null</code>
1130      * @throws IllegalArgumentException if the rangeStyle is invalid
1131      */
1132     public static Iterator<Calendar> iterator(final Date focus, final int rangeStyle) {
1133         if (focus == null) {
1134             throw new IllegalArgumentException("The date must not be null");
1135         }
1136         final Calendar gval = Calendar.getInstance();
1137         gval.setTime(focus);
1138         return iterator(gval, rangeStyle);
1139     }
1140 
1141     /**
1142      * <p>Constructs an <code>Iterator</code> over each day in a date
1143      * range defined by a focus date and range style.</p>
1144      *
1145      * <p>For instance, passing Thursday, July 4, 2002 and a
1146      * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
1147      * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1148      * 2002, returning a Calendar instance for each intermediate day.</p>
1149      *
1150      * <p>This method provides an iterator that returns Calendar objects.
1151      * The days are progressed using {@link Calendar#add(int, int)}.</p>
1152      *
1153      * @param focus  the date to work with, not null
1154      * @param rangeStyle  the style constant to use. Must be one of
1155      * {@link DateUtils#RANGE_MONTH_SUNDAY}, 
1156      * {@link DateUtils#RANGE_MONTH_MONDAY},
1157      * {@link DateUtils#RANGE_WEEK_SUNDAY},
1158      * {@link DateUtils#RANGE_WEEK_MONDAY},
1159      * {@link DateUtils#RANGE_WEEK_RELATIVE},
1160      * {@link DateUtils#RANGE_WEEK_CENTER}
1161      * @return the date iterator, not null
1162      * @throws IllegalArgumentException if the date is <code>null</code>
1163      * @throws IllegalArgumentException if the rangeStyle is invalid
1164      */
1165     public static Iterator<Calendar> iterator(final Calendar focus, final int rangeStyle) {
1166         if (focus == null) {
1167             throw new IllegalArgumentException("The date must not be null");
1168         }
1169         Calendar start = null;
1170         Calendar end = null;
1171         int startCutoff = Calendar.SUNDAY;
1172         int endCutoff = Calendar.SATURDAY;
1173         switch (rangeStyle) {
1174             case RANGE_MONTH_SUNDAY:
1175             case RANGE_MONTH_MONDAY:
1176                 //Set start to the first of the month
1177                 start = truncate(focus, Calendar.MONTH);
1178                 //Set end to the last of the month
1179                 end = (Calendar) start.clone();
1180                 end.add(Calendar.MONTH, 1);
1181                 end.add(Calendar.DATE, -1);
1182                 //Loop start back to the previous sunday or monday
1183                 if (rangeStyle == RANGE_MONTH_MONDAY) {
1184                     startCutoff = Calendar.MONDAY;
1185                     endCutoff = Calendar.SUNDAY;
1186                 }
1187                 break;
1188             case RANGE_WEEK_SUNDAY:
1189             case RANGE_WEEK_MONDAY:
1190             case RANGE_WEEK_RELATIVE:
1191             case RANGE_WEEK_CENTER:
1192                 //Set start and end to the current date
1193                 start = truncate(focus, Calendar.DATE);
1194                 end = truncate(focus, Calendar.DATE);
1195                 switch (rangeStyle) {
1196                     case RANGE_WEEK_SUNDAY:
1197                         //already set by default
1198                         break;
1199                     case RANGE_WEEK_MONDAY:
1200                         startCutoff = Calendar.MONDAY;
1201                         endCutoff = Calendar.SUNDAY;
1202                         break;
1203                     case RANGE_WEEK_RELATIVE:
1204                         startCutoff = focus.get(Calendar.DAY_OF_WEEK);
1205                         endCutoff = startCutoff - 1;
1206                         break;
1207                     case RANGE_WEEK_CENTER:
1208                         startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
1209                         endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
1210                         break;
1211                     default:
1212                         break;
1213                 }
1214                 break;
1215             default:
1216                 throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid.");
1217         }
1218         if (startCutoff < Calendar.SUNDAY) {
1219             startCutoff += 7;
1220         }
1221         if (startCutoff > Calendar.SATURDAY) {
1222             startCutoff -= 7;
1223         }
1224         if (endCutoff < Calendar.SUNDAY) {
1225             endCutoff += 7;
1226         }
1227         if (endCutoff > Calendar.SATURDAY) {
1228             endCutoff -= 7;
1229         }
1230         while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
1231             start.add(Calendar.DATE, -1);
1232         }
1233         while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
1234             end.add(Calendar.DATE, 1);
1235         }
1236         return new DateIterator(start, end);
1237     }
1238 
1239     /**
1240      * <p>Constructs an <code>Iterator</code> over each day in a date
1241      * range defined by a focus date and range style.</p>
1242      *
1243      * <p>For instance, passing Thursday, July 4, 2002 and a
1244      * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
1245      * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1246      * 2002, returning a Calendar instance for each intermediate day.</p>
1247      *
1248      * @param focus  the date to work with, either {@code Date} or {@code Calendar}, not null
1249      * @param rangeStyle  the style constant to use. Must be one of the range
1250      * styles listed for the {@link #iterator(Calendar, int)} method.
1251      * @return the date iterator, not null
1252      * @throws IllegalArgumentException if the date is <code>null</code>
1253      * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar}
1254      */
1255     public static Iterator<?> iterator(final Object focus, final int rangeStyle) {
1256         if (focus == null) {
1257             throw new IllegalArgumentException("The date must not be null");
1258         }
1259         if (focus instanceof Date) {
1260             return iterator((Date) focus, rangeStyle);
1261         } else if (focus instanceof Calendar) {
1262             return iterator((Calendar) focus, rangeStyle);
1263         } else {
1264             throw new ClassCastException("Could not iterate based on " + focus);
1265         }
1266     }
1267     
1268     /**
1269      * <p>Returns the number of milliseconds within the 
1270      * fragment. All datefields greater than the fragment will be ignored.</p>
1271      * 
1272      * <p>Asking the milliseconds of any date will only return the number of milliseconds
1273      * of the current second (resulting in a number between 0 and 999). This 
1274      * method will retrieve the number of milliseconds for any fragment. 
1275      * For example, if you want to calculate the number of milliseconds past today, 
1276      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1277      * be all milliseconds of the past hour(s), minutes(s) and second(s).</p>
1278      * 
1279      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1280      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1281      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1282      * A fragment less than or equal to a SECOND field will return 0.</p> 
1283      * 
1284      * <ul>
1285      *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
1286      *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
1287      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)</li>
1288      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1289      *   (a millisecond cannot be split in milliseconds)</li>
1290      * </ul>
1291      * 
1292      * @param date the date to work with, not null
1293      * @param fragment the {@code Calendar} field part of date to calculate 
1294      * @return number of milliseconds within the fragment of date
1295      * @throws IllegalArgumentException if the date is <code>null</code> or
1296      * fragment is not supported
1297      * @since 2.4
1298      */
1299     public static long getFragmentInMilliseconds(final Date date, final int fragment) {
1300         return getFragment(date, fragment, TimeUnit.MILLISECONDS);    
1301     }
1302     
1303     /**
1304      * <p>Returns the number of seconds within the 
1305      * fragment. All datefields greater than the fragment will be ignored.</p> 
1306      * 
1307      * <p>Asking the seconds of any date will only return the number of seconds
1308      * of the current minute (resulting in a number between 0 and 59). This 
1309      * method will retrieve the number of seconds for any fragment. 
1310      * For example, if you want to calculate the number of seconds past today, 
1311      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1312      * be all seconds of the past hour(s) and minutes(s).</p> 
1313      * 
1314      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1315      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1316      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1317      * A fragment less than or equal to a SECOND field will return 0.</p> 
1318      * 
1319      * <ul>
1320      *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1321      *   (equivalent to deprecated date.getSeconds())</li>
1322      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1323      *   (equivalent to deprecated date.getSeconds())</li>
1324      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
1325      *   (7*3600 + 15*60 + 10)</li>
1326      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1327      *   (a millisecond cannot be split in seconds)</li>
1328      * </ul>
1329      * 
1330      * @param date the date to work with, not null
1331      * @param fragment the {@code Calendar} field part of date to calculate 
1332      * @return number of seconds within the fragment of date
1333      * @throws IllegalArgumentException if the date is <code>null</code> or
1334      * fragment is not supported
1335      * @since 2.4
1336      */
1337     public static long getFragmentInSeconds(final Date date, final int fragment) {
1338         return getFragment(date, fragment, TimeUnit.SECONDS);
1339     }
1340     
1341     /**
1342      * <p>Returns the number of minutes within the 
1343      * fragment. All datefields greater than the fragment will be ignored.</p> 
1344      * 
1345      * <p>Asking the minutes of any date will only return the number of minutes
1346      * of the current hour (resulting in a number between 0 and 59). This 
1347      * method will retrieve the number of minutes for any fragment. 
1348      * For example, if you want to calculate the number of minutes past this month, 
1349      * your fragment is Calendar.MONTH. The result will be all minutes of the 
1350      * past day(s) and hour(s).</p> 
1351      * 
1352      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1353      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1354      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1355      * A fragment less than or equal to a MINUTE field will return 0.</p> 
1356      * 
1357      * <ul>
1358      *  <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1359      *   (equivalent to deprecated date.getMinutes())</li>
1360      *  <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1361      *   (equivalent to deprecated date.getMinutes())</li>
1362      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
1363      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
1364      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1365      *   (a millisecond cannot be split in minutes)</li>
1366      * </ul>
1367      * 
1368      * @param date the date to work with, not null
1369      * @param fragment the {@code Calendar} field part of date to calculate 
1370      * @return number of minutes within the fragment of date
1371      * @throws IllegalArgumentException if the date is <code>null</code> or 
1372      * fragment is not supported
1373      * @since 2.4
1374      */
1375     public static long getFragmentInMinutes(final Date date, final int fragment) {
1376         return getFragment(date, fragment, TimeUnit.MINUTES);
1377     }
1378     
1379     /**
1380      * <p>Returns the number of hours within the 
1381      * fragment. All datefields greater than the fragment will be ignored.</p> 
1382      * 
1383      * <p>Asking the hours of any date will only return the number of hours
1384      * of the current day (resulting in a number between 0 and 23). This 
1385      * method will retrieve the number of hours for any fragment. 
1386      * For example, if you want to calculate the number of hours past this month, 
1387      * your fragment is Calendar.MONTH. The result will be all hours of the 
1388      * past day(s).</p> 
1389      * 
1390      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1391      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1392      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1393      * A fragment less than or equal to a HOUR field will return 0.</p> 
1394      * 
1395      * <ul>
1396      *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1397      *   (equivalent to deprecated date.getHours())</li>
1398      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1399      *   (equivalent to deprecated date.getHours())</li>
1400      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
1401      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
1402      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1403      *   (a millisecond cannot be split in hours)</li>
1404      * </ul>
1405      * 
1406      * @param date the date to work with, not null
1407      * @param fragment the {@code Calendar} field part of date to calculate 
1408      * @return number of hours within the fragment of date
1409      * @throws IllegalArgumentException if the date is <code>null</code> or 
1410      * fragment is not supported
1411      * @since 2.4
1412      */
1413     public static long getFragmentInHours(final Date date, final int fragment) {
1414         return getFragment(date, fragment, TimeUnit.HOURS);
1415     }
1416     
1417     /**
1418      * <p>Returns the number of days within the 
1419      * fragment. All datefields greater than the fragment will be ignored.</p> 
1420      * 
1421      * <p>Asking the days of any date will only return the number of days
1422      * of the current month (resulting in a number between 1 and 31). This 
1423      * method will retrieve the number of days for any fragment. 
1424      * For example, if you want to calculate the number of days past this year, 
1425      * your fragment is Calendar.YEAR. The result will be all days of the 
1426      * past month(s).</p> 
1427      * 
1428      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1429      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1430      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1431      * A fragment less than or equal to a DAY field will return 0.</p> 
1432      *  
1433      * <ul>
1434      *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
1435      *   (equivalent to deprecated date.getDay())</li>
1436      *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
1437      *   (equivalent to deprecated date.getDay())</li>
1438      *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li>
1439      *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li>
1440      *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
1441      *   (a millisecond cannot be split in days)</li>
1442      * </ul>
1443      * 
1444      * @param date the date to work with, not null
1445      * @param fragment the {@code Calendar} field part of date to calculate 
1446      * @return number of days  within the fragment of date
1447      * @throws IllegalArgumentException if the date is <code>null</code> or 
1448      * fragment is not supported
1449      * @since 2.4
1450      */
1451     public static long getFragmentInDays(final Date date, final int fragment) {
1452         return getFragment(date, fragment, TimeUnit.DAYS);
1453     }
1454 
1455     /**
1456      * <p>Returns the number of milliseconds within the 
1457      * fragment. All datefields greater than the fragment will be ignored.</p> 
1458      * 
1459      * <p>Asking the milliseconds of any date will only return the number of milliseconds
1460      * of the current second (resulting in a number between 0 and 999). This 
1461      * method will retrieve the number of milliseconds for any fragment. 
1462      * For example, if you want to calculate the number of seconds past today, 
1463      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1464      * be all seconds of the past hour(s), minutes(s) and second(s).</p> 
1465      * 
1466      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1467      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1468      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1469      * A fragment less than or equal to a MILLISECOND field will return 0.</p> 
1470      * 
1471      * <ul>
1472      *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
1473      *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
1474      *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
1475      *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
1476      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538
1477      *   (10*1000 + 538)</li>
1478      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1479      *   (a millisecond cannot be split in milliseconds)</li>
1480      * </ul>
1481      * 
1482      * @param calendar the calendar to work with, not null
1483      * @param fragment the {@code Calendar} field part of calendar to calculate 
1484      * @return number of milliseconds within the fragment of date
1485      * @throws IllegalArgumentException if the date is <code>null</code> or 
1486      * fragment is not supported
1487      * @since 2.4
1488      */
1489   public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) {
1490     return getFragment(calendar, fragment, TimeUnit.MILLISECONDS);
1491   }
1492     /**
1493      * <p>Returns the number of seconds within the 
1494      * fragment. All datefields greater than the fragment will be ignored.</p> 
1495      * 
1496      * <p>Asking the seconds of any date will only return the number of seconds
1497      * of the current minute (resulting in a number between 0 and 59). This 
1498      * method will retrieve the number of seconds for any fragment. 
1499      * For example, if you want to calculate the number of seconds past today, 
1500      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1501      * be all seconds of the past hour(s) and minutes(s).</p> 
1502      * 
1503      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1504      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1505      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1506      * A fragment less than or equal to a SECOND field will return 0.</p> 
1507      * 
1508      * <ul>
1509      *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1510      *   (equivalent to calendar.get(Calendar.SECOND))</li>
1511      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1512      *   (equivalent to calendar.get(Calendar.SECOND))</li>
1513      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
1514      *   (7*3600 + 15*60 + 10)</li>
1515      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1516      *   (a millisecond cannot be split in seconds)</li>
1517      * </ul>
1518      * 
1519      * @param calendar the calendar to work with, not null
1520      * @param fragment the {@code Calendar} field part of calendar to calculate 
1521      * @return number of seconds within the fragment of date
1522      * @throws IllegalArgumentException if the date is <code>null</code> or 
1523      * fragment is not supported
1524      * @since 2.4
1525      */
1526     public static long getFragmentInSeconds(final Calendar calendar, final int fragment) {
1527         return getFragment(calendar, fragment, TimeUnit.SECONDS);
1528     }
1529     
1530     /**
1531      * <p>Returns the number of minutes within the 
1532      * fragment. All datefields greater than the fragment will be ignored.</p> 
1533      * 
1534      * <p>Asking the minutes of any date will only return the number of minutes
1535      * of the current hour (resulting in a number between 0 and 59). This 
1536      * method will retrieve the number of minutes for any fragment. 
1537      * For example, if you want to calculate the number of minutes past this month, 
1538      * your fragment is Calendar.MONTH. The result will be all minutes of the 
1539      * past day(s) and hour(s).</p> 
1540      * 
1541      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1542      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1543      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1544      * A fragment less than or equal to a MINUTE field will return 0.</p> 
1545      * 
1546      * <ul>
1547      *  <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1548      *   (equivalent to calendar.get(Calendar.MINUTES))</li>
1549      *  <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1550      *   (equivalent to calendar.get(Calendar.MINUTES))</li>
1551      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
1552      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
1553      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1554      *   (a millisecond cannot be split in minutes)</li>
1555      * </ul>
1556      * 
1557      * @param calendar the calendar to work with, not null
1558      * @param fragment the {@code Calendar} field part of calendar to calculate 
1559      * @return number of minutes within the fragment of date
1560      * @throws IllegalArgumentException if the date is <code>null</code> or 
1561      * fragment is not supported
1562      * @since 2.4
1563      */
1564     public static long getFragmentInMinutes(final Calendar calendar, final int fragment) {
1565         return getFragment(calendar, fragment, TimeUnit.MINUTES);
1566     }
1567     
1568     /**
1569      * <p>Returns the number of hours within the 
1570      * fragment. All datefields greater than the fragment will be ignored.</p> 
1571      * 
1572      * <p>Asking the hours of any date will only return the number of hours
1573      * of the current day (resulting in a number between 0 and 23). This 
1574      * method will retrieve the number of hours for any fragment. 
1575      * For example, if you want to calculate the number of hours past this month, 
1576      * your fragment is Calendar.MONTH. The result will be all hours of the 
1577      * past day(s).</p> 
1578      * 
1579      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1580      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1581      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1582      * A fragment less than or equal to a HOUR field will return 0.</p> 
1583      *  
1584      * <ul>
1585      *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1586      *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
1587      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1588      *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
1589      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
1590      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
1591      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1592      *   (a millisecond cannot be split in hours)</li>
1593      * </ul>
1594      *  
1595      * @param calendar the calendar to work with, not null
1596      * @param fragment the {@code Calendar} field part of calendar to calculate 
1597      * @return number of hours within the fragment of date
1598      * @throws IllegalArgumentException if the date is <code>null</code> or 
1599      * fragment is not supported
1600      * @since 2.4
1601      */
1602     public static long getFragmentInHours(final Calendar calendar, final int fragment) {
1603         return getFragment(calendar, fragment, TimeUnit.HOURS);
1604     }
1605     
1606     /**
1607      * <p>Returns the number of days within the 
1608      * fragment. All datefields greater than the fragment will be ignored.</p> 
1609      * 
1610      * <p>Asking the days of any date will only return the number of days
1611      * of the current month (resulting in a number between 1 and 31). This 
1612      * method will retrieve the number of days for any fragment. 
1613      * For example, if you want to calculate the number of days past this year, 
1614      * your fragment is Calendar.YEAR. The result will be all days of the 
1615      * past month(s).</p> 
1616      * 
1617      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1618      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1619      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1620      * A fragment less than or equal to a DAY field will return 0.</p> 
1621      * 
1622      * <ul>
1623      *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
1624      *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
1625      *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
1626      *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
1627      *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28
1628      *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
1629      *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59
1630      *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
1631      *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
1632      *   (a millisecond cannot be split in days)</li>
1633      * </ul>
1634      * 
1635      * @param calendar the calendar to work with, not null
1636      * @param fragment the {@code Calendar} field part of calendar to calculate 
1637      * @return number of days within the fragment of date
1638      * @throws IllegalArgumentException if the date is <code>null</code> or 
1639      * fragment is not supported
1640      * @since 2.4
1641      */
1642     public static long getFragmentInDays(final Calendar calendar, final int fragment) {
1643         return getFragment(calendar, fragment, TimeUnit.DAYS);
1644     }
1645     
1646     /**
1647      * Gets a Date fragment for any unit.
1648      * 
1649      * @param date the date to work with, not null
1650      * @param fragment the Calendar field part of date to calculate 
1651      * @param unit the time unit
1652      * @return number of units within the fragment of the date
1653      * @throws IllegalArgumentException if the date is <code>null</code> or 
1654      * fragment is not supported
1655      * @since 2.4
1656      */
1657     private static long getFragment(final Date date, final int fragment, final TimeUnit unit) {
1658         if(date == null) {
1659             throw  new IllegalArgumentException("The date must not be null");
1660         }
1661         final Calendar calendar = Calendar.getInstance();
1662         calendar.setTime(date);
1663         return getFragment(calendar, fragment, unit);
1664     }
1665 
1666     /**
1667      * Gets a Calendar fragment for any unit.
1668      * 
1669      * @param calendar the calendar to work with, not null
1670      * @param fragment the Calendar field part of calendar to calculate 
1671      * @param unit the time unit
1672      * @return number of units within the fragment of the calendar
1673      * @throws IllegalArgumentException if the date is <code>null</code> or 
1674      * fragment is not supported
1675      * @since 2.4
1676      */
1677     private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) {
1678         if(calendar == null) {
1679             throw  new IllegalArgumentException("The date must not be null"); 
1680         }
1681 
1682         long result = 0;
1683         
1684         final int offset = (unit == TimeUnit.DAYS) ? 0 : 1;
1685         
1686         // Fragments bigger than a day require a breakdown to days
1687         switch (fragment) {
1688             case Calendar.YEAR:
1689                 result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS);
1690                 break;
1691             case Calendar.MONTH:
1692                 result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS);
1693                 break;
1694             default:
1695                 break;
1696         }
1697 
1698         switch (fragment) {
1699             // Number of days already calculated for these cases
1700             case Calendar.YEAR:
1701             case Calendar.MONTH:
1702             
1703             // The rest of the valid cases
1704             case Calendar.DAY_OF_YEAR:
1705             case Calendar.DATE:
1706                 result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS);
1707                 //$FALL-THROUGH$
1708             case Calendar.HOUR_OF_DAY:
1709                 result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES);
1710                 //$FALL-THROUGH$
1711             case Calendar.MINUTE:
1712                 result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS);
1713                 //$FALL-THROUGH$
1714             case Calendar.SECOND:
1715                 result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS);
1716                 break;
1717             case Calendar.MILLISECOND: break;//never useful
1718                 default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported");
1719         }
1720         return result;
1721     }
1722     
1723     /**
1724      * Determines if two calendars are equal up to no more than the specified 
1725      * most significant field.
1726      * 
1727      * @param cal1 the first calendar, not <code>null</code>
1728      * @param cal2 the second calendar, not <code>null</code>
1729      * @param field the field from {@code Calendar}
1730      * @return <code>true</code> if equal; otherwise <code>false</code>
1731      * @throws IllegalArgumentException if any argument is <code>null</code>
1732      * @see #truncate(Calendar, int)
1733      * @see #truncatedEquals(Date, Date, int)
1734      * @since 3.0
1735      */
1736     public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) {
1737         return truncatedCompareTo(cal1, cal2, field) == 0;
1738     }
1739 
1740     /**
1741      * Determines if two dates are equal up to no more than the specified 
1742      * most significant field.
1743      * 
1744      * @param date1 the first date, not <code>null</code>
1745      * @param date2 the second date, not <code>null</code>
1746      * @param field the field from {@code Calendar}
1747      * @return <code>true</code> if equal; otherwise <code>false</code>
1748      * @throws IllegalArgumentException if any argument is <code>null</code>
1749      * @see #truncate(Date, int)
1750      * @see #truncatedEquals(Calendar, Calendar, int)
1751      * @since 3.0
1752      */
1753     public static boolean truncatedEquals(final Date date1, final Date date2, final int field) {
1754         return truncatedCompareTo(date1, date2, field) == 0;
1755     }
1756 
1757     /**
1758      * Determines how two calendars compare up to no more than the specified 
1759      * most significant field.
1760      * 
1761      * @param cal1 the first calendar, not <code>null</code>
1762      * @param cal2 the second calendar, not <code>null</code>
1763      * @param field the field from {@code Calendar}
1764      * @return a negative integer, zero, or a positive integer as the first 
1765      * calendar is less than, equal to, or greater than the second.
1766      * @throws IllegalArgumentException if any argument is <code>null</code>
1767      * @see #truncate(Calendar, int)
1768      * @see #truncatedCompareTo(Date, Date, int)
1769      * @since 3.0
1770      */
1771     public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) {
1772         final Calendar truncatedCal1 = truncate(cal1, field);
1773         final Calendar truncatedCal2 = truncate(cal2, field);
1774         return truncatedCal1.compareTo(truncatedCal2);
1775     }
1776 
1777     /**
1778      * Determines how two dates compare up to no more than the specified 
1779      * most significant field.
1780      * 
1781      * @param date1 the first date, not <code>null</code>
1782      * @param date2 the second date, not <code>null</code>
1783      * @param field the field from <code>Calendar</code>
1784      * @return a negative integer, zero, or a positive integer as the first 
1785      * date is less than, equal to, or greater than the second.
1786      * @throws IllegalArgumentException if any argument is <code>null</code>
1787      * @see #truncate(Calendar, int)
1788      * @see #truncatedCompareTo(Date, Date, int)
1789      * @since 3.0
1790      */
1791     public static int truncatedCompareTo(final Date date1, final Date date2, final int field) {
1792         final Date truncatedDate1 = truncate(date1, field);
1793         final Date truncatedDate2 = truncate(date2, field);
1794         return truncatedDate1.compareTo(truncatedDate2);
1795     }
1796 
1797 
1798     //-----------------------------------------------------------------------
1799     /**
1800      * <p>Date iterator.</p>
1801      */
1802     static class DateIterator implements Iterator<Calendar> {
1803         private final Calendar endFinal;
1804         private final Calendar spot;
1805         
1806         /**
1807          * Constructs a DateIterator that ranges from one date to another. 
1808          *
1809          * @param startFinal start date (inclusive)
1810          * @param endFinal end date (inclusive)
1811          */
1812         DateIterator(final Calendar startFinal, final Calendar endFinal) {
1813             super();
1814             this.endFinal = endFinal;
1815             spot = startFinal;
1816             spot.add(Calendar.DATE, -1);
1817         }
1818 
1819         /**
1820          * Has the iterator not reached the end date yet?
1821          *
1822          * @return <code>true</code> if the iterator has yet to reach the end date
1823          */
1824         @Override
1825         public boolean hasNext() {
1826             return spot.before(endFinal);
1827         }
1828 
1829         /**
1830          * Return the next calendar in the iteration
1831          *
1832          * @return Object calendar for the next date
1833          */
1834         @Override
1835         public Calendar next() {
1836             if (spot.equals(endFinal)) {
1837                 throw new NoSuchElementException();
1838             }
1839             spot.add(Calendar.DATE, 1);
1840             return (Calendar) spot.clone();
1841         }
1842 
1843         /**
1844          * Always throws UnsupportedOperationException.
1845          * 
1846          * @throws UnsupportedOperationException
1847          * @see java.util.Iterator#remove()
1848          */
1849         @Override
1850         public void remove() {
1851             throw new UnsupportedOperationException();
1852         }
1853     }
1854 
1855 }